package com.baijiaoxi.utils.excel;

import com.baijiaoxi.common.constant.DateTime;
import com.baijiaoxi.common.constant.DateTimeFormatStr;
import com.baijiaoxi.common.model.concurrent.BaiExecutors;
import com.baijiaoxi.common.model.excel.Column;
import com.baijiaoxi.common.utils.jdk.Strings;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;

@Slf4j
public class CvsExportHelper {
    List<Column> columns;
    private Gson gson = new GsonBuilder().setDateFormat(DateTimeFormatStr.FULL_DATE).create();
    private CsvDataSource dataSource;
    private ExecutorService executorService = BaiExecutors.create("csvExport", 2, 4);
    private int size=50;

    public CvsExportHelper setSize(int size) {
        this.size = size;
        return this;
    }

    public CvsExportHelper(CsvDataSource dataSource) {
        this.dataSource = dataSource;
        this.columns = new ArrayList<>();
    }

    public CvsExportHelper(CsvDataSource dataSource, List<Column> columns) {
        this.dataSource = dataSource;
        this.columns = columns;
    }

    public CvsExportHelper addColumns(List<Column> columns) {
        this.columns.addAll(columns);
        return this;
    }


    public CvsExportHelper addColumn(Column column) {
        this.columns.add(column);
        return this;
    }


    public CvsExportHelper addColumn(String key, String title) {
        addColumn(new Column(key, title));
        return this;
    }


    public CvsExportHelper addColumn(String key, String title, int width) {
        addColumn(new Column(key, title, width));
        return this;
    }

    public <T> CvsExportHelper addColumn(String key, String title, Map<T, String> reflectMap) {
        addColumn(new Column(key, title, reflectMap));
        return this;
    }

    <T> StringBuffer appendRecord(StringBuffer stringBuffer, T record, List<Column> columns) {
        columns.stream().forEach(column -> {
            String value = getRealValue(record, column);
            if (value.contains(","))
                value = value.replace(",", ";");
            stringBuffer.append("\t").append(value).append("\t,");
        });
        return stringBuffer;
    }

    private <T> String getRealValue(T record, Column column) {
        String originValue = getOriginValue(record, column);
        Map<String, String> reflectMap = column.getReflectMap();
        if (reflectMap != null && reflectMap.containsKey(originValue)) {
            String realValue = reflectMap.get(originValue);
            if (realValue == null)
                return "";
            return realValue;
        } else if (!originValue.isEmpty() && Strings.isNotBlank(column.getDateFormat())) {
            try {
                return new SimpleDateFormat(column.getDateFormat()).
                        format(DateTime.FULL_DATE.parse(originValue));
            } catch (Exception e) {
                log.error(String.format("时间转换错误:%s，%s:\n", record.toString(), column.toString()), e);
            }
        }
        return originValue;
    }

    private <T> String getOriginValue(T record, Column column) {
        if (record == null)
            return "";
        if (column == null)
            return "";
        if (Strings.isBlank(column.getKey()))
            return "";
        JsonElement resElement = null;
        JsonObject tempObject = gson.toJsonTree(record).getAsJsonObject();
        if (column.getKey().contains(".")) {
            String[] keyArr = column.getKey().split("\\.");
            JsonElement tempElement = tempObject;
            for (int i = 0; i < keyArr.length; i++) {
                if (tempObject == null) {
                    break;
                } else
                    tempElement = getJsonElement(tempElement, keyArr[i]);
            }
            resElement = tempElement;
        } else {
            JsonElement jsonElement = tempObject.get(column.getKey());
            if (jsonElement != null)
                resElement = jsonElement;
        }
        return resElement == null ? "" : resElement.getAsString();
    }


    JsonElement getJsonElement(JsonElement jsonElement, String key) {
        if (jsonElement == null)
            return null;
        JsonElement resElement = jsonElement.getAsJsonObject().get(key);
        if (resElement == null)
            return null;
        else return resElement;
    }

    public <Q> byte[] export(Q query) throws InterruptedException {
        StringBuffer sb = new StringBuffer(new String(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}));
        //处理表头
        columns.stream().forEach(column -> sb.append(column.getTitle()).append(","));
        int pageIndex = 1;
        // 按分页处理行
        IPage iPage = dataSource.getRecordPages(query, pageIndex, size);
        long pages = iPage.getPages();
        if (pages == 1) {
            sb.append(Strings.LINE_SEPARATOR);
            iPage.getRecords().stream().forEach(record -> {
                appendRecord(sb, record, columns);
                sb.append(Strings.LINE_SEPARATOR);
            });
        } else if (pages > 1) {
            int pagesInt = Long.valueOf(pages).intValue();
            StringBuffer[] bufferArr = new StringBuffer[pagesInt];
            //大于1页
            CountDownLatch countDownLatch = new CountDownLatch(pagesInt);
            IPage finalIPage = iPage;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    StringBuffer sb = new StringBuffer();
                    sb.append(Strings.LINE_SEPARATOR);
                    finalIPage.getRecords().stream().forEach(record -> {
                        appendRecord(sb, record, columns);
                        sb.append(Strings.LINE_SEPARATOR);
                    });
                    bufferArr[0] = sb;
                    countDownLatch.countDown();
                }
            });
            for (pageIndex = 2; pageIndex <= pages; pageIndex++) {
                int finalPageIndex = pageIndex;
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        StringBuffer sb = new StringBuffer();
                        IPage iPage = dataSource.getRecordPages(query, finalPageIndex, size);
                        iPage.getRecords().stream().forEach(record -> {
                            appendRecord(sb, record, columns);
                            sb.append(Strings.LINE_SEPARATOR);
                        });
                        bufferArr[finalPageIndex-1] = sb;
                        countDownLatch.countDown();
                    }
                });
            }
            countDownLatch.await();
            Arrays.asList(bufferArr).stream().forEach(
                    buffer -> sb.append(buffer));
        }
        return sb.toString().getBytes(StandardCharsets.UTF_8);
    }


}
